﻿using Jinndev.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

namespace Jinndev.Modding {

    /// <summary>
    /// 模组管理器<br/>
    /// 在引用其他配置时，sid格式为(mod名).(配置名)，如果不填mod名则默认是当前mod，填写其他mod名则可以引用其他mod种的配置
    /// </summary>
    public static class ModManager {

        private static Dictionary<string, ConfigEntry> configEntries = new Dictionary<string, ConfigEntry>();
        private static List<Type> subConfigTypeList = new List<Type>();

        private static Dictionary<string, Mod> modDict = new Dictionary<string, Mod>(); // name-Mod
        private static Dictionary<string, Config> allConfigDict = new Dictionary<string, Config>(); // sid->Config
        private static List<Config> allConfigList = new List<Config>();    // 所有配置按顺序存储，第一个存储一个空配置，用来占用id=0，所以正常配置从1开始

        private static MultiMap<int, Config> entryConfigMap = new MultiMap<int, Config>(); // 按照ConfigEntry分组存放所有Config
        private static MultiMap<Type, SubConfig> subConfigMap = new MultiMap<Type, SubConfig>(); // 按照SubConfig类型，分组存放所有SubConfig

        private static IConfigEntryProvider configEntryProvider = null;
        private static List<IConfigValidator> validators = new List<IConfigValidator>();

        public static bool Initialized { get; private set; }

        /// <summary>
        /// 初始化，将加载streamingAssetsPath/Mod/下的文件夹
        /// </summary>
        public static void Init(IEnumerable<ConfigEntry> configEntries = null, IEnumerable<Type> subConfigTypes = null) {
            if (Initialized) {
                Debug.LogError("[ModManager] ModManager was already initialized");
                return;
            }
            Initialized = true;

            // 找到IConfigEntryProvider实例
            FindConfigEntryProvider();

            // 如果没有提供，则尝试自动获取
            if (configEntries == null) {
                configEntries = LoadConfigEntries();
            }
            if (configEntries != null) {
                // 按照ConfigEntry.fileNamePrefix存储ConfigEntry
                foreach (ConfigEntry entry in configEntries) {
                    if (!ModManager.configEntries.ContainsKey(entry.fileNamePrefix.ToLower())) {
                        ModManager.configEntries.Add(entry.fileNamePrefix.ToLower(), entry);
                    }
                    else {
                        Debug.LogError($"[ModManager] entry.fileNamePrefix alread exists: {entry.fileNamePrefix}");
                    }
                }
            }
            else {
                Debug.LogError("[ModManager] ConfigEntry collection not found, use IConfigEntryProvider or pass in directly");
            }


            // 初始化subConfigTypes
            if (subConfigTypes != null) {
                subConfigTypeList.AddRange(subConfigTypes);
            }
            else {
                LoadSubConfigTypes();
            }

            // 加载验证器
            Type[] validatorTypes = CommonUtil.GetTypesByInterface(AppDomain.CurrentDomain, typeof(IConfigValidator));
            foreach (Type type in validatorTypes) {
                try {
                    validators.Add(Activator.CreateInstance(type) as IConfigValidator);
                }
                catch (Exception e) {
                    Debug.LogException(e);
                }
            }
        }

        /// <summary>
        /// 开始加载
        /// </summary>
        public static void Load() {
            if (!Initialized) {
                throw new Exception("ModManager isn't initialized");
            }
            // 加载Mod
            List<string> modDirs = ModUtil.ListModDirs();
            foreach (string dir in modDirs) {
                Mod mod = ModUtil.LoadMod(dir);
                if (!modDict.ContainsKey(mod.name)) {
                    modDict.Add(mod.name, mod);
                    Debug.Log($"[ModManager] Load mod: {mod.name}");
                }
                else {
                    Debug.LogError($"[ModManager] Mod name already exists: {mod.name}");
                }
            }

            // 加载Config
            foreach (Mod mod in modDict.Values) {
                LoadConfigs(mod, true);
            }
            // 初始化Config
            foreach (Config config in allConfigList) {
                config.Init();
                config.InitSubConfigs(subConfigTypeList);
            }
        }

        private static IEnumerable<ConfigEntry> LoadConfigEntries() {
            return configEntryProvider != null ? configEntryProvider.GetEntries() : null;
        }

        private static void FindConfigEntryProvider() {
            Type[] types = CommonUtil.GetTypesByInterface(AppDomain.CurrentDomain, typeof(IConfigEntryProvider));
            if (types.Length > 0) {
                Type type = types[0];
                try {
                    configEntryProvider = Activator.CreateInstance(type) as IConfigEntryProvider;
                }
                catch (Exception e) {
                    Debug.LogException(e);
                }
            }
        }

        private static void LoadSubConfigTypes() {
            Type[] types = CommonUtil.GetTypesByParentType(AppDomain.CurrentDomain, typeof(SubConfig));

            if (types.Length > 0) {
                subConfigTypeList.AddRange(types);

                List<string> typeNames = new List<string>(subConfigTypeList.Count);
                foreach (Type type in subConfigTypeList) {
                    typeNames.Add(type.Name);
                }
                Debug.Log($"[ModManager] SubConfig types: {string.Join(", ", typeNames)}");
            }
            else {
                Debug.Log($"[ModManager] SubConfig implementation not found");
            }
        }

        /// <summary>
        /// 加载Mod下的配置
        /// </summary>
        /// <param name="store">是否存储Config</param>
        public static void LoadConfigs(Mod mod, bool store) {
            string configDir = Path.Combine(mod.path, "Config");
            string[] files = ResourceManager.GetFiles(configDir, "*.json", SearchOption.AllDirectories);
            mod.ClearConfig();

            foreach (string file in files) {
                string fileName = Path.GetFileNameWithoutExtension(file);
                string prefix = CommonUtil.SubStringTo(fileName, "_").ToLower();
                string suffix = CommonUtil.SubStringFrom(fileName, "_");
                if (configEntries.TryGetValue(prefix, out ConfigEntry entry)) {
                    LoadConfigEntry(mod, entry, suffix, file, store);
                }
                else {
                    Debug.LogError($"[ModManager] Invalid config entry prefix: {prefix}");
                }
            }
        }

        private static void LoadConfigEntry(Mod mod, ConfigEntry entry, string suffix, string file, bool store) {
            JArray array;
            try {
                string text = ResourceManager.ReadAllText(file);
                array = JArray.Parse(text);
            }
            catch (Exception e) {
                Debug.LogException(e);
                Debug.LogError($"[ModManager] Parse config fail: {e.Message}, {file}");
                return;
            }

            // 解析配置数组
            foreach (JObject json in array) {
                Config config;
                try {
                    config = LoadConfig(mod, json, entry, file, store);
                }
                catch (Exception e) {
                    Debug.LogException(e);
                    //Debug.LogError($"Load config fail: {e.Message}, {filePath}");
                    continue;
                }
                if (config == null) {
                    continue;
                }
                config.suffix = suffix;

                if (store) {
                    // 解析成功，自定义验证无误后开始存储
                    if (!ValidateConfig(config)) {
                        continue;
                    }

                    // mod中存放一份
                    mod.AddConfig(config);

                    // 按照<sid, config>存放一份
                    allConfigDict.Add(config.sid, config);

                    // id从1开始，按照<id, config>存放一份
                    config.id = allConfigList.Count + 1;
                    allConfigList.Add(config);

                    // 按照ConfigEntry存放一份
                    entryConfigMap.Add(entry.category, config);

                    // 按照SubConfig类型存放一份
                    subConfigMap.AddRange(config.SubConfigs);
                }
                else {
                    mod.AddConfig(config);
                }
            }
        }

        public static bool ValidateConfig(Config config) {
            foreach (IConfigValidator validator in validators) {
                if (!validator.Validate(config)) {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 加载一条配置
        /// </summary>
        /// <param name="mod"></param>
        /// <param name="json">该配置的json段</param>
        /// <param name="entry"></param>
        /// <returns></returns>
        public static Config LoadConfig(Mod mod, JObject json, ConfigEntry entry, string file, bool store) {
            string name = JsonUtil.GetString(json, "name");
            if (string.IsNullOrWhiteSpace(name)) {
                Debug.LogError($"[ModManager] Config name is required, {mod.name}: {Path.GetFileName(file)}");
                return null;
            }
            if (name.Contains(".")) {
                Debug.LogError($"[ModManager] Invalid character '.' in config name, {mod.name}: {name}");
                return null;
            }
            Config config = Config.Create(mod, entry, name.Trim(), file);

            if (store && allConfigDict.ContainsKey(config.sid)) {
                Debug.LogError($"[ModManager] Config name already exists: {config.sid}");
                return null;
            }

            config.icon = JsonUtil.GetString(json, "icon");

            LoadSubConfig(json, config);

            return config;
        }

        private static void LoadSubConfig(JObject json, Config config) {
            foreach (Type type in subConfigTypeList) {
                string name = ModUtil.GetSubConfigNodeName(type);
                //JToken value = json.SelectToken(name);
                if (json.TryGetValue(name, StringComparison.CurrentCultureIgnoreCase, out JToken value)) {
                    try {
                        SubConfig subConfig = value.ToObject(type) as SubConfig;
                        if (subConfig != null) {
                            config.AddSubConfig(subConfig);
                        }
                    }
                    catch (Exception e) {
                        Debug.LogException(new Exception($"[ModManager] Load sub config error \"{config.name} - {name}\"", e));
                    }
                }
            }
            //foreach(var prop in json.Properties()) {
            //    Type type = ModUtil.GetSubConfigByName(prop.Name);
            //    if(type == null) {
            //        Debug.Log(prop.Name);
            //    }
            //}
        }

        public static IReadOnlyList<Mod> GetModList() {
            return modDict.Values.ToList();
        }

        public static IReadOnlyList<Config> GetAllConfigList() {
            return allConfigList;
        }

        public static List<Config> GetConfigList(params ConfigEntry[] entries) {
            List<Config> result = new List<Config>();
            if (entries != null) {
                foreach (ConfigEntry entry in entries) {
                    if (entryConfigMap.TryGetValues(entry.category, out List<Config> list)) {
                        result.AddRange(list);
                    }
                }
            }
            return result;
        }

        public static List<Config> GetConfigList(params int[] categories) {
            List<Config> result = new List<Config>();
            if (categories != null) {
                foreach (int category in categories) {
                    if (entryConfigMap.TryGetValues(category, out List<Config> list)) {
                        result.AddRange(list);
                    }
                }
            }
            return result;
        }

        public static List<T> GetSubConfigList<T>() where T : SubConfig {
            List<T> result = new List<T>();
            if (subConfigMap.TryGetValues(typeof(T), out List<SubConfig> list)) {
                foreach (SubConfig subConfig in list) {
                    result.Add(subConfig as T);
                }
            }
            return result;
        }

        public static List<SubConfig> GetSubConfigList(params Type[] types) {
            List<SubConfig> result = new List<SubConfig>();
            if (types != null) {
                foreach (Type type in types) {
                    if (subConfigMap.TryGetValues(type, out List<SubConfig> list)) {
                        result.AddRange(list);
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// id从1开始
        /// </summary>
        public static Config GetConfig(int id) {
            if (id < 1 || id > allConfigList.Count) {
                //Debug.LogError($"[ModManager] Config index out of range: {id}");
                return null;
            }
            return allConfigList[id - 1];
        }

        /// <summary>
        /// 根据完整sid查找config
        /// </summary>
        public static Config GetConfig(string sid) {
            if (sid == null) {
                return null;
            }
            if (allConfigDict.TryGetValue(sid, out Config config)) {
                return config;
            }
            //Debug.LogError($"[ModManager] Config not found: {sid}");
            return null;
        }

        /// <summary>
        /// 如果传入完整sid，直接获取<br/>
        /// 如果传入name，先从当前mod中查找，然后从其他mod中查找
        /// </summary>
        public static Config FindConfig(string nameOrSid, Mod curMod) {
            if (nameOrSid == null) {
                return null;
            }
            // 如果是完整sid，直接获取
            if (allConfigDict.TryGetValue(nameOrSid, out Config config)) {
                return config;
            }
            // 尝试从当前mod中获取
            if (curMod != null) {
                config = curMod.GetConfig(nameOrSid);
                if (config != null) {
                    return config;
                }
            }
            // 尝试从其他mod中获取
            foreach (Mod mod in modDict.Values) {
                if (mod != curMod) {
                    config = mod.GetConfig(nameOrSid);
                    if (config != null) {
                        return config;
                    }
                }
            }
            //Debug.LogError($"[ModManager] Config not found: {nameOrSid}");
            return null;
        }

        public static T GetSubConfig<T>(string sid) where T : SubConfig {
            Config config = GetConfig(sid);
            if (config != null) {
                return config.GetSubConfig<T>();
            }
            return null;
        }

        public static T GetSubConfig<T>(int id) where T : SubConfig {
            Config config = GetConfig(id);
            if (config != null) {
                return config.GetSubConfig<T>();
            }
            return null;
        }

        public static bool TryGetSubConfig<T>(string sid, out Config config, out T subConfig) where T : SubConfig {
            config = GetConfig(sid);
            if (config != null) {
                subConfig = config.GetSubConfig<T>();
                return true;
            }
            subConfig = null;
            return false;
        }

        public static bool TryGetSubConfig<T>(int id, out Config config, out T subConfig) where T : SubConfig {
            config = GetConfig(id);
            if (config != null) {
                subConfig = config.GetSubConfig<T>();
                return true;
            }
            subConfig = null;
            return false;
        }

        public static Dictionary<string, ConfigEntry>.ValueCollection GetEntries() {
            return configEntries.Values;
        }

        public static IConfigEntryProvider ConfigEntryProvider {
            get { return configEntryProvider; }
        }

        public static string IdToSid(int id) {
            Config config = GetConfig(id);
            return config != null ? config.sid : null;
        }

        public static int SidToId(string sid) {
            Config config = GetConfig(sid);
            return config != null ? config.id : Config.EmptyID;
        }

        public static int NameToId(string nameOrSid, Mod curMod) {
            Config config = FindConfig(nameOrSid, curMod);
            return config != null ? config.id : Config.EmptyID;
        }

        [Obsolete]
        public static int GetId(string sid) {
            Config config = GetConfig(sid);
            return config != null ? config.id : Config.EmptyID;
        }

        [Obsolete]
        public static string GetSid(int id) {
            Config config = GetConfig(id);
            return config != null ? config.sid : null;
        }

    }

}
